package com.nexus.all.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hankcs.hanlp.tokenizer.StandardTokenizer;
import com.mongodb.client.result.UpdateResult;
import com.nexus.all.dto.NoteDTO;
import com.nexus.all.dto.NoteLikeDTO;
import com.nexus.all.dto.NoteQueryDTO;
import com.nexus.all.enums.CommentTypeEnum;
import com.nexus.all.enums.NoteLikeTypeEnum;
import com.nexus.all.manager.*;
import com.nexus.all.model.*;
import com.nexus.all.mongodb.model.MongoNoteHistory;
import com.nexus.all.mongodb.repository.NoteHistoryRepository;
import com.nexus.all.service.NoteService;
import com.nexus.all.vo.NoteVO;
import com.nexus.all.vo.UserVO;
import com.nexus.bean.constants.ThreadNameConstant;
import com.nexus.bean.exeption.ServiceException;
import com.nexus.bean.model.UserInfoVO;
import com.nexus.bean.query.QueryResultVO;
import com.nexus.bean.result.ResultCode;
import com.nexus.bean.utils.BeanUtils;
import com.nexus.bean.utils.CollectionsUtil;
import com.nexus.bean.utils.StringUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @ClassName: NoteServiceImpl
 * @Author: ZZYR
 * @Date: 2024/2/26  11:08
 * @Version 1.0.0
 **/
@Slf4j
@Service
@AllArgsConstructor
public class NoteServiceImpl implements NoteService {

    private final NoteManager noteManager;
    private final NoteContentManager noteContentManager;
    private final UserManager userManager;
    private final NoteLikeManager noteLikeManager;
    private final NoteLikeViewManager noteLikeViewManager;
    private final NoteWordManager noteWordManager;
    private final CommentViewManager commentViewManager;
    private final NoteHistoryRepository noteHistoryRepository;
    private final MongoTemplate mongoTemplate;

    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackFor = Exception.class)
    public Long addNote(NoteDTO noteDTO) {

        //判断用户是否设置封面如果没有则使用详情图片第一张作为封面，如果详情也没有上传则使用系统默认图片
        if (StringUtils.isBlank(noteDTO.getCoverUrl())) {
            //判断用户是否上传详情图片
            if (StringUtils.isBlank(noteDTO.getNoteContentDTO().getContentIms())) {
                //设置系统默认图片
                noteDTO.setCoverUrl("https://copywritings.oss-accelerate.aliyuncs.com/boot/nexus-all/images/2024-03-04/cd35f96c766840838a7d7f761498e93a.png");
            } else {
                List<String> contentImgs = Arrays.asList(noteDTO.getNoteContentDTO().getContentIms().split(";"));
                //校验是否大于6张图片
                if (contentImgs.size() > 6) {
                    throw new ServiceException(ResultCode.COMMON_MESSAGE, "笔记图片最多只能上传6张");
                }
                //截取详情图片里第一张用作封面
                noteDTO.setCoverUrl(contentImgs.get(0));
            }
        }

        //保存笔记内容
        NoteContent noteContent = BeanUtils.beanCopy(noteDTO.getNoteContentDTO(), NoteContent.class);
        noteContentManager.save(noteContent);
        Long contentId = noteContent.getId();

        //保存笔记
        noteDTO.setContentId(contentId);
        Note note = BeanUtils.beanCopy(noteDTO, Note.class);
        noteManager.save(note);

        //异步生效其实是Spring拿到当前对象的代理对象，这里如果直接调用就不是Spring的代理对象了事务就会失效，所以要获取当前对象的代理对象
        NoteServiceImpl proxy = (NoteServiceImpl) AopContext.currentProxy();
        //异步处理生成标题分词
        proxy.generationNoteWords(CollectionsUtil.asList(note));
        return note.getId();
    }

    @Override
    public QueryResultVO<NoteVO> queryNotes(NoteQueryDTO dto) {

        List<String> words = new ArrayList<>();
        if (StringUtils.isNotBlank(dto.getKeyword())) {
            //关键词不为空的话，进行分词查询分词表获取笔记ids
            words = StandardTokenizer.segment(dto.getKeyword()).stream().map((term) -> {
                return term.word;
            }).distinct().collect(Collectors.toList());

            List<NoteWord> noteWordList = noteWordManager.getByWords(words);
            if (CollectionsUtil.isNotEmpty(noteWordList)) {
                List<Long> noteIds = noteWordList.stream()
                        //flatMap()方法将noteWord.getNoteIds()中的流与主流合并
                        .flatMap(noteWord -> noteWord.getNoteIds().stream())
                        .distinct()
                        .collect(Collectors.toList());

                dto.setNoteIds(noteIds);
                dto.setKeyword(null);
            } else {
                //最后一道防线，通过keyword去模糊查询，速度可能比较慢
                words = new ArrayList<>();
            }
        }

        Page<Note> page = noteManager.queryNotes(dto);
        QueryResultVO<NoteVO> resultVO = BeanUtils.pageToQueryResultVO(page, NoteVO.class);

        List<NoteVO> records = BeanUtils.beanCopy(page.getRecords(), NoteVO.class);
        if (CollectionsUtil.isEmpty(records)) {
            resultVO.setRecords(new ArrayList<>());
            return resultVO;
        }


        Map<Long, NoteLike> noteLikeMap = new HashMap<>();
        if (Objects.nonNull(dto.getUserId())) {
            //登录，需要查是否点赞
            List<Long> nodeIds = records.stream().map(NoteVO::getId).collect(Collectors.toList());
            List<NoteLike> noteLikeList = noteLikeManager.isLiked(nodeIds, NoteLikeTypeEnum.NOTE.getValue(), dto.getUserId());
            noteLikeMap = noteLikeList.stream().collect(Collectors.toMap(NoteLike::getLikeDataId, Function.identity()));
        }


        List<Long> userIds = records.stream().map(NoteVO::getUserId).distinct().collect(Collectors.toList());
        List<User> userList = userManager.listByIds(userIds);
        Map<Long, User> userMap = userList.stream().collect(Collectors.toMap(User::getId, Function.identity()));

        List<Long> noteIds = records.stream().map(NoteVO::getId).distinct().collect(Collectors.toList());
        List<NoteLikeView> noteLikeViewList = noteLikeViewManager.getByLikeDataIds(noteIds, NoteLikeTypeEnum.NOTE.getValue());
        Map<Long, NoteLikeView> noteLikeViewMap = noteLikeViewList.stream().collect(Collectors.toMap(NoteLikeView::getLikeDataId, Function.identity()));

        for (NoteVO noteVO : records) {
            //设置用户昵称
            User user = userMap.get(noteVO.getUserId());
            noteVO.setNickName(user.getNickName());
            noteVO.setAvatarUrl(user.getAvatarUrl());

            //设置点赞数量
            NoteLikeView noteLikeView = noteLikeViewMap.get(noteVO.getId());
            if (Objects.nonNull(noteLikeView)) {
                noteVO.setLikeCount(noteLikeView.getLikeCount());
            } else {
                noteVO.setLikeCount(0);
            }

            for (String word : words) {
                //关键字搜索，关键字加粗高亮展示
                String formattedKeyword = String.format("<b>%s</b>", word);
                noteVO.setTitle(noteVO.getTitle().replaceAll(word, formattedKeyword));
            }
            //如果关键词没有检索到，就通过模糊查询做匹配，所以这里模糊查询也要做高亮
            if (StringUtils.isNotBlank(dto.getKeyword())) {
                String formattedKeyword = String.format("<b>%s</b>", dto.getKeyword());
                noteVO.setTitle(noteVO.getTitle().replaceAll(dto.getKeyword(), formattedKeyword));
            }

            //是否点赞过
            if (Objects.nonNull(noteLikeMap.get(noteVO.getId()))) {
                noteVO.setIsLiked(Boolean.TRUE);
            }
        }

        resultVO.setRecords(records);
        return resultVO;
    }

    @Override
    public NoteVO getNoteDetail(Long noteId, UserInfoVO user) {
        Note note = noteManager.getById(noteId);
        NoteVO noteVO = BeanUtils.beanCopy(note, NoteVO.class);

        NoteContent content = noteContentManager.getById(note.getContentId());
        noteVO.setNotecontent(content);

        User noteUser = userManager.getById(note.getUserId());
        noteVO.setUserVO(BeanUtils.beanCopy(noteUser, UserVO.class));


        //是否点赞过
        if (Objects.nonNull(user)) {
            Boolean isLiked = noteLikeManager.isLiked(noteId, NoteLikeTypeEnum.NOTE.getValue(), user.getId());
            noteVO.setIsLiked(isLiked);
        }


        //设置点赞数量
        NoteLikeView noteLikeView = noteLikeViewManager.getByLikeDataId(note.getId(), NoteLikeTypeEnum.NOTE.getValue());
        if (Objects.nonNull(noteLikeView)) {
            noteVO.setLikeCount(noteLikeView.getLikeCount());
        } else {
            noteVO.setLikeCount(0);
        }

        //设置笔记评论数量
        CommentView commentView = commentViewManager.getByCommentDataId(note.getId(), CommentTypeEnum.NOTE.getValue());
        if (Objects.nonNull(commentView)) {
            noteVO.setCommentCount(commentView.getCommentCount());
        } else {
            noteVO.setCommentCount(0);
        }

        //通过mongodb保存浏览量
        incrementBrowseNum(noteId);

        return noteVO;
    }

    public void incrementBrowseNum(long noteId) {
        Query query = new Query().addCriteria(Criteria.where("noteId").is(noteId));
        Update update = new Update().inc("browseNum", 1);
        UpdateResult updateResult = mongoTemplate.upsert(query, update, MongoNoteHistory.class);
        /*if (updateResult.getModifiedCount() == 0) {
            MongoNoteHistoryService newHistory = new MongoNoteHistoryService();
            newHistory.setNoteId(noteId);
            newHistory.setBrowseNum(1);
            noteHistoryRepository.save(newHistory);
        }*/
    }

    @Override
    public void addLike(NoteLikeDTO dto) {
        Boolean isLiked = noteLikeManager.isLiked(dto.getLikeDataId(), dto.getNoteLikeType(), dto.getUserId());
        if (isLiked) {
            log.info("已点赞，重复点赞,数据:{}", JSON.toJSONString(dto));
            return;
        }

        NoteLike noteLike = new NoteLike();
        noteLike.setLikeDataId(dto.getLikeDataId());
        noteLike.setNoteLikeType(dto.getNoteLikeType());
        noteLike.setUserId(dto.getUserId());
        noteLikeManager.save(noteLike);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackFor = Exception.class)
    public void initNoteWord() {
        List<Note> noteList = noteManager.list();

        //异步生效其实是Spring拿到当前对象的代理对象，这里如果直接调用就不是Spring的代理对象了事务就会失效，所以要获取当前对象的代理对象
        NoteServiceImpl proxy = (NoteServiceImpl) AopContext.currentProxy();
        proxy.generationNoteWords(noteList);
    }


    @Async(value = ThreadNameConstant.ASYNC_SERVICE_EXECUTOR)
    public void generationNoteWords(List<Note> noteList) {
        log.info("异步生成笔记关键字 开始 笔记入参:{}", JSON.toJSONString(noteList));
        Map<String, List<Long>> wordsMap = new HashMap<>();
        for (Note note : noteList) {

            List<String> words = StandardTokenizer.segment(note.getTitle()).stream().map((term) -> {
                return term.word;
            }).distinct().collect(Collectors.toList());
            for (String word : words) {
                if (StringUtils.isBlank(word)) {
                    continue;
                }
                List<Long> noteIds = Optional.ofNullable(wordsMap.get(word)).orElse(new ArrayList<>());
                noteIds.add(note.getId());
                wordsMap.put(word, noteIds);
            }
        }

        if(CollectionsUtil.isEmpty(wordsMap)){
            return;
        }

        List<NoteWord> existNoteWordList = noteWordManager.getByWords(wordsMap.keySet().stream().collect(Collectors.toList()));
        Map<String, NoteWord> noteWordMap = existNoteWordList.stream().collect(Collectors.toMap(NoteWord::getNoteWord, Function.identity()));

        for (Map.Entry<String, List<Long>> wordsMapEntry : wordsMap.entrySet()) {
            NoteWord noteWord = noteWordMap.get(wordsMapEntry.getKey());
            if (Objects.isNull(noteWord)) {
                noteWord = new NoteWord();
                noteWord.setNoteWord(wordsMapEntry.getKey());
                noteWord.setNoteIds(wordsMapEntry.getValue());
                noteWordMap.put(wordsMapEntry.getKey(), noteWord);
            } else {
                for (Long noteId : wordsMapEntry.getValue()) {
                    if (!noteWord.getNoteIds().contains(noteId)) {
                        noteWord.getNoteIds().add(noteId);
                    }
                }
            }
        }

        noteWordManager.saveOrUpdateBatch(noteWordMap.values());
        log.info("异步生成笔记关键字 结束");
    }
}
